linux学习笔记(五)编译内核模块生成ko驱动文件 您所在的位置:网站首页 c 编译命令 linux学习笔记(五)编译内核模块生成ko驱动文件

linux学习笔记(五)编译内核模块生成ko驱动文件

2023-12-14 14:35| 来源: 网络整理| 查看: 265

系列文章目录

linux学习笔记(五)编译内核模块生成ko驱动文件

文章目录 系列文章目录前言一、加载内核简介二、第一个hello world文件1.文件树2.hello.c3.Makefile 三 编译命令总结互相交流

前言

系统:ubuntu 1604 功能:加载内核模块

提示:以下是本篇文章正文内容,下面案例可供参考

一、加载内核简介

Linux内核的整体架构本就非常庞大,其包含的组件也非常多。而我们怎样把需要的部分都包含在内核中呢?一种方法是把所有需要的功能都编译到Linux内核中。这会导致两个问题,一是生成的内核会很大,二是如果我们要在现有的内核中新增或删除功能,将不得不重新编译内核。

Linux提供了这样的机制,这种机制被称为模块(Module)。可使得编译出的内核本身并不需要包含所有功能,而在这些功能需要被使用的时候,其对应的代码被动态地加载到内核中。 模块本身不被编译入内核映像,从而控制了内核的大小。·模块一旦被加载,它就和内核中的其他部分完全一样。 这篇文章提供了最简单的一个内核模块工程,第一个hello world工程。

而且是单独编译到内核中,方便安装和删除。

二、第一个hello world文件 1.文件树 ├── hello.c ├── Makefile

整个工程只有两个文件,一个hello.c,一个用来控制编译的Makefile。

2.hello.c

代码如下(示例):

#include #include static int __init hello_init(void) { printk(KERN_INFO "Hello World enter\n"); return 0; } module_init(hello_init); static void __exit hello_exit(void) { printk(KERN_INFO "Hello World exit\n "); } module_exit(hello_exit); MODULE_AUTHOR("Barry Song "); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("A simple Hello World Module"); MODULE_ALIAS("a simplest module");

代码解析: 内核模块中用于输出的函数是内核空间的printk()而不是用户空间的printf(),printk()的用法和printf()基本相似,但前者可定义输出级别。printk()可作为一种最基本的内核调试手段。

一个Linux内核模块主要由如下几个部分组成。 (1)模块加载函数 当通过insmod或modprobe命令加载内核模块时,模块的加载函数会自动被内核执行,完成本模块的相关初始化工作。 Linux内核模块加载函数一般以__init标识声明,模块加载函数以“module_init(函数名)”的形式被指定。它返回整型值,若初始化成功,应返回0。而在初始化失败时,应该返回错误编码。在Linux内核里,错误编码是一个接近于0的负值。 (2)模块卸载函数 当通过rmmod命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块卸载函数相反的功能。 Linux内核模块加载函数一般以__exit标识声明,模块卸载函数在模块卸载的时候执行,而不返回任何值,且必须以“module_exit(函数名)”的形式来指定。 (3)模块许可证声明许可证(LICENSE) 声明描述内核模块的许可权限,如果不声明LICENSE,模块被加载时,将收到内核被污染(Kernel Tainted)的警告。在Linux内核模块领域,可接受的LICENSE包括“GPL”、“GPL v2”、“GPLand additional rights”、“Dual BSD/GPL”、“Dual MPL/GPL”和“Proprietary”(关于模块是否可以采用非GPL许可权,如“Proprietary”,这个在学术界和法律界都有争议)。大多数情况下,内核模块应遵循GPL兼容许可权。Linux内核模块最常见的是以MODULE_LICENSE(“GPL v2”)语句声明模块采用GPL v2。 (4)模块参数(可选) 模块参数是模块被加载的时候可以传递给它的值,它本身对应模块内部的全局变量。 (5)模块导出符号(可选) 内核模块可以导出的符号(symbol,对应于函数或变量),若导出,其他模块则可以使用本模块中的变量或函数。 (6)模块作者等信息声明(可选)

MODULE_AUTHOR、MODULE_DESCRIPTION、MODULE_VERSION、MODULE_DEVICE_TABLE、MODULE_ALIAS分别声明模块的作者、描述、版本、设备表和别名 (7)printk printk的日志级别定义如下(在include/linux/kernel.h中):

#define KERN_EMERG 0   #define KERN_ALERT 1   #define KERN_CRIT 2   #define KERN_ERR 3   #define KERN_WARNING 4   #define KERN_NOTICE 5   #define KERN_INFO 6   #define KERN_DEBUG 7

3.Makefile KVERS = $(shell uname -r) #shell中输出uname -r会获得内核版本号,这里将版本号存到变量KVERS # Kernel modules #将后面的东东编译为“内核模块”, obj-y 编译进内核,obj-n 不编译。 obj-m += hello.o # 开启EXTRA_CFLAGS=-g-O0,可以得到包含调试信息的hello.ko模块。 #EXTRA_CFLAGS=-g -O0 #-C 表示让 make 命令进入指定的内核源代码目录 build: kernel_modules kernel_modules: make -C /lib/modules/$(KVERS)/build M=$(CURDIR) modules clean: make -C /lib/modules/$(KVERS)/build M=$(CURDIR) clean

:=”表示:它的右边如果为变量,那么该变量在这条语句之前就要定义好,而不能在使用这条语句之后定义的变量 “=”表示:当它右边如果变量时,这个变量可以在这条语句之前或者之后使用 “?=”表示:当它左边的变量在这条语句之前没有定义过,则执行本条语句,如果已经定义了,则什么都不做。

三 编译命令 make #make的提示信息中,CC是编译,LD是链接 modinfo hello.ko#获得模块的信息 sudo insmod hello.ko #或者是insmod ./hello.ko #加载时输出“Hello World enter” lsmod #获得系统中已加载的所有模块以及模块间的依赖关系 sudo rmmod hello #卸载时输出“Hello World exit”。 lsmod #获得系统中已加载的所有模块以及模块间的依赖关系 总结

以上代码,在lsmod中确实可以看到能够加载内核模块 ,但是不知道为什么无法打印输出信息。改了printk的输出级别也不管用。有网友知道的可以告诉我。

互相交流

读者你好!如果你对本文内容感兴趣,我十分希望能够和你互相学习,可以扫码和我联系!一起进步

在这里插入图片描述



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有